package com.chukun.interview.feature.newswitch;

import java.util.Calendar;

/**
 * @author chukun
 * @version 1.0.0
 * @description java新特性 switch表达式
 * switch 表达式在 JDK 14 正式发布
 *
 * switch表达式与switch语句的区别:
 * 1、break 语句只能出现在 switch 语句里，不能出现在 switch 表达式里.
 * 2、yield 语句只能出现在 switch 表达式里，不能出现在 switch 语句里。
 * 3、switch 表达式需要穷举出所有的情景，而 switch 语句不需要情景穷举。
 * 4、使用冒号标识符的 switch 语句，支持情景间的 fall-through, 而使用箭头标识符的 switch语句只会匹配一中case。 {@link SwitchTemplate#getMonthDay02()}
 * 5、使用箭头标识符的 switch 表达式，一个 case 语句支持多个情景,而使用冒号标识符的 switch 表达式不支持多情景的 case 语句。{@link SwitchTemplate#getMonthDayWithSwitchExpression02()}
 *
 * @createTime 2022年06月05日 23:30:00
 */
public class SwitchTemplate {

    public static void main(String[] args) {

        // 正常的switch语句
        System.out.println(getMonthDay());

        // 带箭头的switch语句
        System.out.println(getMonthDay02());

        // switch表达式
        System.out.println(getMonthDayWithSwitchExpression());

        // 使用冒号的switch表达式
        System.out.println(getMonthDayWithSwitchExpression02());
    }


    /**
     * 判断每个月有多少天
     * 使用了 switch 语句
     *  1.第一个容易犯错的地方，就是在 break 关键字的使用上。
     *  2.第二个容易犯错的地方，是反复出现的赋值语句
     */

    public static int getMonthDay() {
        Calendar today = Calendar.getInstance();
        int month = today.get(Calendar.MONTH);
        int year = today.get(Calendar.YEAR);
        int daysInMonth;
        switch (month) {
            case Calendar.JANUARY:
            case Calendar.MARCH:
            case Calendar.MAY:
            case Calendar.JULY:
            case Calendar.AUGUST:
            case Calendar.OCTOBER:
            case Calendar.DECEMBER:
                daysInMonth = 31;
                break;
            case Calendar.APRIL:
            case Calendar.JUNE:
            case Calendar.SEPTEMBER:
            case Calendar.NOVEMBER:
                daysInMonth = 30;
                break;
            case Calendar.FEBRUARY:
                if (((year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0)) {
                    daysInMonth = 29;
                } else {
                    daysInMonth = 28;
                }
                break;
            default:
                throw new RuntimeException("Calendar in JDK does not work");
        }
        return daysInMonth;
    }

    /**
     * 使用带箭头的switch语句
     * break 语句没有出现在下一个 case 语句之前。这也就意味着，使用箭头标识符的 switch 语句不再需要 break 语句来实现情景间的代码共享了
     * @return
     */
    public static int getMonthDay02() {
        Calendar today = Calendar.getInstance();
        int month = today.get(Calendar.MONTH);
        int year = today.get(Calendar.YEAR);
        int daysInMonth;
        switch (month) {
            case Calendar.JANUARY,
                 Calendar.MARCH,
                 Calendar.MAY,
                 Calendar.JULY,
                 Calendar.AUGUST,
                 Calendar.OCTOBER,
                 Calendar.DECEMBER ->  daysInMonth = 31;
            case Calendar.APRIL,
                 Calendar.JUNE,
                 Calendar.SEPTEMBER,
                 Calendar.NOVEMBER ->  daysInMonth = 30;
            case Calendar.FEBRUARY -> {
                if (((year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0)) {
                    daysInMonth = 29;
                } else {
                    daysInMonth = 28;
                }
            }
            default ->  throw new RuntimeException("Calendar in JDK does not work");
        }
        return daysInMonth;
    }

    /**
     * 基于switch表达式，改进代码逻辑，推荐使用
     *
     * switch表达式变化:
     * 1、switch 代码块出现在了赋值运算符的右侧
     * 2、是多情景的合并。也就是说，一个 case 语句，可以处理多个情景。这些情景，使用逗号分隔开来，共享一个代码块.
     * 3、新的情景操作符，"->"，它是一个箭头标识符。这个符号使用在 case 语句里，一般化的形式是"case L ->".
     * 4、箭头标识符右侧的数值。这个数值，代表的就是该匹配情景下，switch 表达式的数值。需要注意的是，箭头标识符右侧可以是表达式、代码块或者异常抛出语句，而不能是其他的形式.
     * 5、出现了一个新的关键字 "yield", yield 只能用在 switch 表达式里
     *       大多数情况下，switch 表达式箭头标识符的右侧是一个数值或者是一个表达式。 如果需要一个或者多个语句，就要使用代码块的形式,
     *       这时候，就需要引入一个新的 yield 语句来产生一个值，这个值就成为这个封闭代码块代表的数值。
     *
     * @return
     */
    public static int getMonthDayWithSwitchExpression() {
        Calendar today = Calendar.getInstance();
        int month = today.get(Calendar.MONTH);
        int year = today.get(Calendar.YEAR);

        int daysInMonth = switch (month) {
            case Calendar.JANUARY,
                 Calendar.MARCH,
                 Calendar.MAY,
                 Calendar.JULY,
                 Calendar.AUGUST,
                 Calendar.OCTOBER,
                 Calendar.DECEMBER -> 31;
            case Calendar.APRIL,
                 Calendar.JUNE,
                 Calendar.SEPTEMBER,
                 Calendar.NOVEMBER  -> 30;
             case Calendar.FEBRUARY -> {
                 if (((year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0)) {
                     yield 29;
                 } else {
                     yield  28;
                 }
             }
            default ->  throw new RuntimeException("Calendar in JDK does not work");
        };

        return daysInMonth;
    }

    /**
     * 使用冒号的switch表达式
     * @return
     */
    public static int getMonthDayWithSwitchExpression02() {
        Calendar today = Calendar.getInstance();
        int month = today.get(Calendar.MONTH);
        int year = today.get(Calendar.YEAR);

        int daysInMonth = switch (month) {
            case Calendar.JANUARY:
            case Calendar.MARCH:
            case Calendar.MAY:
            case Calendar.JULY:
            case Calendar.AUGUST:
            case Calendar.OCTOBER:
            case Calendar.DECEMBER:
                yield 31;
            case Calendar.APRIL:
            case Calendar.JUNE:
            case Calendar.SEPTEMBER:
            case Calendar.NOVEMBER:
                yield 30;
            case Calendar.FEBRUARY: {
                if (((year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0)) {
                    yield 29;
                } else {
                    yield  28;
                }
            }
            default:  throw new RuntimeException("Calendar in JDK does not work");
        };
        return daysInMonth;
    }

    /**
     * switch 的模式匹配这个特性，在 JDK 17 中以预览版的形式发布。目前不可使用，后面在补充
     * 使用switch表达式，增强代码的可维护性。
     * @param shape
     * @return
     */
//    public static boolean isSquare(SealedShape shape) {
//        return switch (shape) {
//            case null, Circle c -> false;
//            case PermitsSquare s -> true;
//            case PermitsRectangle r -> r.length() == r.width();
//        };
//    }


}
